package modeler;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.HashMap;

import javax.media.opengl.GL;
import javax.media.opengl.glu.*;

/**
 * Light object.  Also includes functions to map light objects to OpenGL lights
 * @author ags
 */
public class Light extends SceneNode { 

  private static final long serialVersionUID = 3688792483416912692L;

  public static final float AMBIENT_CONSTANT = 0.05f;
  public static final float DIFFUSE_CONSTANT = 0.4f;
  
  /** Constant light parameters */
  public final float[] ambient = new float[] { AMBIENT_CONSTANT, AMBIENT_CONSTANT, AMBIENT_CONSTANT, 1 };
  public final float[] diffuse = new float[] { DIFFUSE_CONSTANT, DIFFUSE_CONSTANT, DIFFUSE_CONSTANT, 1 };
  public final float[] specular = new float[] { 2, 2, 2, 1 };
  public final float[] position = new float[] { 6, 8, 10, 1 };
  
  /** Map of all 8 GL lights */
  protected final static boolean[] taken = { false, false, false, false, false, false, false, false };
  protected final static int[] ids = { GL.GL_LIGHT0, GL.GL_LIGHT1, GL.GL_LIGHT2, GL.GL_LIGHT3, GL.GL_LIGHT4, GL.GL_LIGHT5, GL.GL_LIGHT6, GL.GL_LIGHT7 };

  /** This lights ID and index in the GL map */
  public int id;
  protected int index;

  /** quadric for drawing the light */
  protected transient GLUquadric quad;

  /**
   * Required for IO
   */
  protected Light() {}
  
  /**
   * Constructor
   * @param newIndex
   */
  private Light(int newIndex) {

    index = newIndex;
    if (index != -1) {
      id = ids[index];
    }
    diffuse[0] = DIFFUSE_CONSTANT * specular[0];
    diffuse[1] = DIFFUSE_CONSTANT * specular[1];
    diffuse[2] = DIFFUSE_CONSTANT * specular[2];
    ambient[0] = AMBIENT_CONSTANT * specular[0];
    ambient[1] = AMBIENT_CONSTANT * specular[1];
    ambient[2] = AMBIENT_CONSTANT * specular[2];
    setName("Light " + index);
  }
  
  /**
   * Create a new light which is on or off depending on enabled.
   * @param enabled
   * @return
   */
  public static synchronized Light makeNewLight(boolean enabled) {

    if (!enabled) {
      return new Light(-1);
    }

    for (int ctr = 0; ctr < taken.length; ctr++) {
      if (taken[ctr] == false) {
        taken[ctr] = true;
        return new Light(ctr);
      }
    }
    return null;
  }

  /**
   * Enable the lights in the scene
   */
  public static synchronized void turnOnLights(GL gl) {

    for (int ctr = 0; ctr < taken.length; ctr++) {
      if (taken[ctr]) {
        gl.glEnable(ids[ctr]);
      }
      else {
        gl.glDisable(ids[ctr]);
      }
    }
  }

  /**
   * Unmap a light from the OpenGL map
   * @param l
   */
  public static synchronized void returnLight(Light l) {

    taken[l.index] = false;
  }

  /**
   * Draws a light
   */
  public void draw(GL gl, GLU glu, Camera cam) {

    if(quad == null) {
      quad = glu.gluNewQuadric();
    }  

    float scale = 0.05f * cam.getHeight();
    gl.glPushAttrib(GL.GL_ALL_ATTRIB_BITS);
    gl.glDisable(GL.GL_LIGHTING);
    gl.glPushMatrix();
    gl.glTranslatef(position[0], position[1], position[2]);
    gl.glScalef(scale, scale, scale);
    gl.glColor3f(0.8f,0.7f,0.1f);
    glu.gluSphere(quad, 1, 4, 2);
    gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
    gl.glLineWidth(1f);
    gl.glScalef(1.03f, 1.03f, 1.03f);
    gl.glColor3f(0,0,0);
    glu.gluSphere(quad, 1, 4, 2);
    gl.glLineWidth(1);
    gl.glPopMatrix();
    gl.glPopAttrib();

    
  }
  
  /**
   * @throws IOException 
   * @see modeler.SceneNode#readData(java.io.ObjectInputStream)
   */
  protected void readData(ObjectInputStream in) throws IOException {

    id = in.readInt();
    index = in.readInt();
    if(index >= 0)
      taken[index] = true;
    position[0] = in.readFloat();
    position[1] = in.readFloat();
    position[2] = in.readFloat();
    diffuse[0] = in.readFloat();
    diffuse[1] = in.readFloat();
    diffuse[2] = in.readFloat();
    specular[0] = in.readFloat();
    specular[1] = in.readFloat();
    specular[2] = in.readFloat();
    ambient[0] = in.readFloat();
    ambient[1] = in.readFloat();
    ambient[2] = in.readFloat();
    
  }

  /**
   * @throws IOException 
   * @see modeler.SceneNode#saveData(java.io.ObjectOutputStream)
   */
  protected void saveData(ObjectOutputStream out) throws IOException {

    out.writeInt(id);
    out.writeInt(index);
    out.writeFloat(position[0]);
    out.writeFloat(position[1]);
    out.writeFloat(position[2]);
    out.writeFloat(diffuse[0]);
    out.writeFloat(diffuse[1]);
    out.writeFloat(diffuse[2]);
    out.writeFloat(specular[0]);
    out.writeFloat(specular[1]);
    out.writeFloat(specular[2]);
    out.writeFloat(ambient[0]);
    out.writeFloat(ambient[1]);
    out.writeFloat(ambient[2]);
    
  }

  /**
   * @see modeler.SceneNode#exportMainData(java.util.HashMap, java.io.PrintWriter)
   */
  protected void exportMainData(HashMap materialsMap, PrintWriter pw) {

    pw.println("  <light>");
    pw.println("    <location>"+position[0]+" "+position[1]+" "+position[2]+"</location>");
    pw.println("    <power>"+specular[0]+" "+specular[1]+" "+specular[2]+"</power>");
    pw.println("  </light>");
    
  }
  
  /**
   * @see javax.swing.tree.TreeNode#getAllowsChildren()
   */
  public boolean getAllowsChildren() {

    return false;

  }

  /**
   * @see javax.swing.tree.TreeNode#isLeaf()
   */
  public boolean isLeaf() {

    return true;

  }

public float[] getAmbient() {
	return ambient;
}

public float[] getDiffuse() {
	return diffuse;
}

public float[] getSpecular() {
	return specular;
}

public float[] getPosition() {
	return position;
}
  
}
